#import "oalPlayback.h"
#import "MyOpenALSupport.h"


@implementation oalPlayback

@synthesize _sources;
@synthesize _buffers;
@synthesize isPlaying;
@synthesize wasInterrupted;
@synthesize iPodIsPlaying;


void interruptionListener(void*	inClientData, UInt32 inInterruptionState)
{
	oalPlayback* THIS = (oalPlayback*)inClientData;
	if (inInterruptionState == kAudioSessionBeginInterruption)
	{
		alcMakeContextCurrent(NULL);
		
		THIS.wasInterrupted = YES;
	}
	else if (inInterruptionState == kAudioSessionEndInterruption)
	{
		OSStatus result = AudioSessionSetActive(true);
		if (result)
		{
			NSLog(@"Error setting audio session active! %d\n", result);
		}

		alcMakeContextCurrent(THIS->context);

		if (THIS.wasInterrupted)
		{
			//[THIS startSound];			
			THIS.wasInterrupted = NO;
		}
	}
}

void RouteChangeListener(void* inClientData, AudioSessionPropertyID	inID, UInt32 inDataSize, const void* inData)
{
	CFDictionaryRef dict = (CFDictionaryRef)inData;
	
	CFStringRef oldRoute = (CFStringRef)CFDictionaryGetValue(dict, CFSTR(kAudioSession_AudioRouteChangeKey_OldRoute));
	
	UInt32 size = sizeof(CFStringRef);
	
	CFStringRef newRoute;
	OSStatus result = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &size, &newRoute);

	NSLog(@"result: %d Route changed from %@ to %@", result, oldRoute, newRoute);
}


static oalPlayback* g_oalPlaybackInstance = nil;

+ (oalPlayback*) getSharedInstance
{
	if (g_oalPlaybackInstance == nil)
	{
		g_oalPlaybackInstance = [[oalPlayback alloc] init]; // assignment not done here
	}
	
	
	return g_oalPlaybackInstance;
}
								
- (id) init
{	
	if (self = [super init]) 
	{		
		// setup our audio session
		OSStatus result = AudioSessionInitialize(NULL, NULL, interruptionListener, self);
		if (result)
		{
			NSLog(@"Error initializing audio session! %d\n", result);
		}
		else 
		{
			// if there is other audio playing, we don't want to play the background music
			UInt32 size = sizeof(iPodIsPlaying);
			result = AudioSessionGetProperty(kAudioSessionProperty_OtherAudioIsPlaying, &size, &iPodIsPlaying);
			if (result)
			{
				NSLog(@"Error getting other audio playing property! %d", result);
			}

			// if the iPod is playing, use the ambient category to mix with it
			// otherwise, use solo ambient to get the hardware for playing the app background track
			UInt32 category = (iPodIsPlaying) ? kAudioSessionCategory_AmbientSound : kAudioSessionCategory_SoloAmbientSound;
						
			result = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
			if (result) NSLog(@"Error setting audio session category! %d\n", result);

			result = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, RouteChangeListener, self);
			if (result) NSLog(@"Couldn't add listener: %d", result);

			result = AudioSessionSetActive(true);
			if (result) NSLog(@"Error setting audio session active! %d\n", result);
		}
		
		_bg_music_player = [AVAudioPlayer alloc];
		
		self._sources = [[[NSMutableArray alloc] init] autorelease];
		self._buffers = [[[NSMutableDictionary alloc] init] autorelease];
		
		wasInterrupted = NO;
		
		// Initialize our OpenAL environment
		[self initOpenAL];
	}
	
	return self;
}

- (void) dealloc
{
	[super dealloc];

	[self releaseOpenAL];
	
	[_bg_music_url release];
	[_bg_music_player release];
}

- (void) initOpenAL
{
	ALenum error;
	
	// Create a new OpenAL Device
	// Pass NULL to specify the system’s default output device
	device = alcOpenDevice(NULL);
	if (device != NULL)
	{
		// Create a new OpenAL Context
		// The new context will render to the OpenAL Device just created 
		context = alcCreateContext(device, 0);
		if (context != NULL)
		{
			// Make the new context the Current OpenAL Context
			alcMakeContextCurrent(context);
		}
	}
	// clear any errors
	alGetError();
	
	[self initSourceArray];
}

- (void) releaseOpenAL
{	
	// Delete the Sources
	for (NSNumber* sourceNumber in _sources) 
	{
		NSUInteger source = [sourceNumber unsignedIntegerValue];
		alDeleteSources(1, &source);
	}
	
	// Delete the Buffers
	for (NSNumber* bufferNumber in _buffers) 
	{
		NSUInteger buffer = [bufferNumber unsignedIntegerValue];
		alDeleteBuffers(1, &buffer);
	}
	
    //Release context
    alcDestroyContext(context);
    //Close device
    alcCloseDevice(device);
}

- (void) initSourceArray
{
	ALenum error = AL_NO_ERROR;
	alGetError(); // Clear the error
	
	for (int i = 0; i < _MAX_SOUND_SOURCE; i++)
	{
		NSUInteger source;
		
		// Create some OpenAL Source Objects
		alGenSources(1, &source);
		if (alGetError() != AL_NO_ERROR) 
		{
			NSLog(@"Error generating sources! %x\n", error);  
		}
		
		//alSourcef(source, AL_PITCH, 1.0f);
		//alSourcef(source, AL_GAIN, 1.0f);
		//alSourcef(source, AL_REFERENCE_DISTANCE, 50.0f);
		// Turn Looping OFF
		//alSourcei(source, AL_LOOPING, AL_FALSE);
		
		//not set buffer here
		
		if ((error = alGetError()) != AL_NO_ERROR) 
		{
			NSLog(@"Error attaching buffer to source: %x\n", error);
		}
		
		//add source to _sources array
		[_sources addObject:[NSNumber numberWithUnsignedInt: source]];
	}
}

- (void)loadMusic:(NSString*) filename
{
	_bg_music_url = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:filename ofType:@""]];
	
	_bg_music_player = [_bg_music_player initWithContentsOfURL:_bg_music_url error:nil];
		
}

- (void)setMusicVolume:(float) vol
{
	if (_bg_music_player)
	{
		_bg_music_player.volume = vol;
	}
}

- (void)startMusic:(NSInteger) numberOfLoops
{
	if (_bg_music_player && (!iPodIsPlaying))
	{
		_bg_music_player.numberOfLoops = numberOfLoops;
		[_bg_music_player play];
		_bg_music_player.meteringEnabled = YES;
	}
}

- (void)stopMusic
{
	if (_bg_music_player) 
	{
		[_bg_music_player stop];
	}	
}

- (BOOL)isMusicPlaying
{
	if (_bg_music_player) 
	{
		return _bg_music_player.playing;
	}
	
	return NO;
}

- (float)getPeakMusicPower: (int) channel
{
	float db;
	[ _bg_music_player updateMeters ];
	db = [ _bg_music_player peakPowerForChannel: channel ];
	return db;
}

- (float)getAverangeMusicPower: (int) channel
{
	float db;
	[ _bg_music_player updateMeters ];
	db = [ _bg_music_player averagePowerForChannel: channel ];
	return db;
}


- (void)loadSound:(NSString*)filename soundID:(NSInteger)sound_id
{
	ALenum  error = AL_NO_ERROR;
	ALvoid* data;
	ALuint buffer;
	
	// Create some OpenAL Buffer Objects
	alGenBuffers(1, &buffer);
	
	if ((error = alGetError()) != AL_NO_ERROR) 
	{
		NSLog(@"Error Generating Buffers: %x", error);
	}
			
	
	ALenum  format;
	ALsizei size;
	ALsizei freq;
	
	NSBundle* bundle = [NSBundle mainBundle];
	
	// get some audio data from a wave file
	CFURLRef fileURL = (CFURLRef)[[NSURL fileURLWithPath:[bundle pathForResource:filename ofType:@""]] retain];
	
	if (fileURL)
	{	
		data = GetOpenALAudioData(fileURL, &size, &format, &freq);
		CFRelease(fileURL);
		
		if ((error = alGetError()) != AL_NO_ERROR) 
		{
			NSLog(@"error loading sound: %x\n", error);
		}
		
		// use the static buffer data API
		alBufferDataStaticProc(buffer, format, data, size, freq);
		
		if ((error = alGetError()) != AL_NO_ERROR) 
		{
			NSLog(@"error attaching audio to buffer: %x\n", error);
		}
		
		[_buffers setObject:[NSNumber numberWithUnsignedInt:buffer] forKey:[NSNumber numberWithUnsignedInt:sound_id]];
		
	}
	else
	{
		NSLog(@"Could not find file!\n");
	}
}

- (void)startSound: (NSInteger)sound_id
{
	ALenum error;
	ALenum state;
	
	for (NSNumber* sourceNumber in _sources) 
	{
		NSUInteger source = [sourceNumber unsignedIntegerValue];
		
	    alGetSourcei(source, AL_SOURCE_STATE, &state);
	 
	    if (state != AL_PLAYING)
		{
			//get buffer
			NSNumber* bufferNumber = [_buffers objectForKey:[NSNumber numberWithUnsignedInt:sound_id]];
			
			if (bufferNumber != nil)
			{
				NSUInteger buffer = [bufferNumber unsignedIntegerValue];
				
				// Turn Looping OFF
				alSourcei(source, AL_LOOPING, AL_FALSE);
				
				// Set Source Reference Distance
				alSourcef(source, AL_REFERENCE_DISTANCE, 50.0f);
				
				// attach the buffer to the source
				alSourcei(source, AL_BUFFER, buffer);
				
				//play sound
				alSourcePlay(source);
				
				if ((error = alGetError()) != AL_NO_ERROR)
				{
					NSLog(@"error starting source: %x\n", error);
				}
				
				return;
			}
		}
	}
}

- (void)stopAllSound
{
	ALenum error;
	
	for (NSNumber* sourceNumber in _sources) 
	{
		NSUInteger source = [sourceNumber unsignedIntegerValue];
		
		// Stop playing
		alSourceStop(source);
		if ((error = alGetError()) != AL_NO_ERROR) 
		{
			NSLog(@"error stopping source: %x\n", error);
		}
	}
}

- (void)setSourcePos:(NSUInteger)source x:(NSInteger)xx y:(NSInteger)yy distance:(NSInteger)dd
{
	// Position of the source sound.
	ALfloat SourcePos[] = { xx, dd, yy };

	// Move our audio source coordinates
	alSourcefv(source, AL_POSITION, SourcePos);
}

- (void)setListenerPos
{
	// Position of the Listener.
	ALfloat ListenerPos[] = { 0.0, 0.0, 0.0 };

	// Velocity of the Listener.
	ALfloat ListenerVel[] = { 0.0, 0.0, 0.0 };

	// Orientation of the Listener. (first 3 elements are "at", second 3 are "up")
	// Also note that these should be units of '1'.
	ALfloat ListenerOri[] = { 0.0, 0.0, -1.0,  0.0, 1.0, 0.0 };

	alListenerfv(AL_POSITION,    ListenerPos);
	alListenerfv(AL_VELOCITY,    ListenerVel);
	alListenerfv(AL_ORIENTATION, ListenerOri);
}

@end

void OS_LoadMusic(const char* filename)
{
	NSString* fname = [[NSString alloc] initWithUTF8String: filename];
	
	[[oalPlayback getSharedInstance] loadMusic: fname];
}

void OS_PlayCurrentMusic(int num_loop)
{
	[[oalPlayback getSharedInstance] startMusic: num_loop];
}

void OS_StopCurrentMusic()
{
	[[oalPlayback getSharedInstance] stopMusic];
}
void OS_PauseCurrentMusic()
{
	[[oalPlayback getSharedInstance] stopMusic];
}

bool OS_IsCurrentMusicPlaying()
{
	return [[oalPlayback getSharedInstance] isMusicPlaying];
}


float OS_getPeakMusicPower(int channel)
{
	return [[oalPlayback getSharedInstance] getPeakMusicPower: channel];
}

float OS_getAverangeMusicPower(int channel)
{
	return [[oalPlayback getSharedInstance] getAverangeMusicPower: channel];
}

float OS_LoadSFX(const char* filename, int sound_id)
{
	NSString* fname = [[NSString alloc] initWithUTF8String: filename];
	
	[[oalPlayback getSharedInstance] loadSound:fname soundID:sound_id];
}

void OS_PlaySFX(int sound_id)
{
	[[oalPlayback getSharedInstance] startSound:sound_id];
}

void OS_StopAllSFX()
{
	[[oalPlayback getSharedInstance] stopAllSound];
}